﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Windows.Threading;
using System.Windows;
using System.Text.RegularExpressions;
using avlib_wrapper;

namespace dmm
{

    public class Encoder
    {
        static int MAX_TASK_COUNT = 4;

        public delegate void TaskFinishedCallback(Task t);

        public List<Task> tasks = new List<Task>();
        //active_tasks contain running tasks,include paused ones.
        private Dictionary<uint, Task> active_tasks = new Dictionary<uint, Task>();
        private int running_count = 0;
        public Dispatcher dispatcher = null;

        //console exeutor
        private ExecutorWrapper exe = new ExecutorWrapper();
        //console exe callback
        private EncoderCallback callback = null;
        //UI callback
        private TaskFinishedCallback finishedFunc = null;

        #region task management

        public Encoder(TaskFinishedCallback f)
        {
            finishedFunc = f;
            dispatcher = Application.Current.Dispatcher;
        }



        public void start_task(Task t)
        {
            switch (t.state)
            {
                case TaskState.TaskState_Wait:
                case TaskState.TaskState_Canceled:
                case TaskState.TaskState_Error:

                    //get params from ProfileManager
                    string cmd = t.get_commandline();

                    string prog = Options.Instance.EncoderPath;
                    Debug.Write("Mencoder command is:\n" + cmd + "\n");
                    if (callback == null)
                    {

                        callback = new EncoderCallback(this);
                    }


                    //construct console command line
                    //execute console command,and return the task_id,
                    //also set callback function
                    uint id = exe.exec(prog, cmd,Options.Instance.WorkPath, callback, new IntPtr(0), false);

                    //add to active
                    t.State = TaskState.TaskState_Active;
                    t.Error = 0;
                    running_count++;
                    active_tasks.Add(id, t);
                    break;
                case TaskState.TaskState_Active:
                case TaskState.TaskState_Paused:
                    break;
            }

        }

        public void stop_all()
        {
            foreach (var kvp in active_tasks)
            {
                exe.stop(kvp.Key);
            }
        }

        public void stop_task(uint tid)
        {
            exe.stop(tid);
        }
        public void stop_task(Task t)
        {
            switch (t.state)
            {
                case TaskState.TaskState_Wait:
                case TaskState.TaskState_Canceled:
                case TaskState.TaskState_Error:
                    break;
                case TaskState.TaskState_Active:
                case TaskState.TaskState_Paused:
                    {
                        foreach (KeyValuePair<uint, Task> kvp in active_tasks)
                        {
                            if (kvp.Value == t)
                            {
                                exe.stop(kvp.Key);
                                break;
                            }
                        }
                    }
                    break;
            }

        }


        public void delete_task(Task t)
        {
            stop_task(t);
            tasks.Remove(t);
        }

        public void add_task(Task task)
        {
            Debug.Assert(task.state == TaskState.TaskState_Wait);
            tasks.Add(task);
            if (running_count < MAX_TASK_COUNT)
            {
                start_task(task);
            }
        }
        private void schedule()
        {
            foreach (Task t in tasks)
            {
                if (running_count >= MAX_TASK_COUNT) break;
                if (t.state == TaskState.TaskState_Wait)
                    start_task(t);
            }
        }
        #endregion

        public void addErrorString(uint tid,string err_str)
        {
            active_tasks[tid].error_string += err_str;
        }

        public void onTaskExit(uint tid, int code)
        {
            running_count--;

            if (code == 0)//successful
            {
                active_tasks[tid].State = TaskState.TaskState_Done;
                active_tasks[tid].Progress = 100;

                if (finishedFunc != null) finishedFunc(active_tasks[tid]);
            }
            else
            {
                active_tasks[tid].State = TaskState.TaskState_Error;
                active_tasks[tid].Error = code;
                System.IO.File.Delete(active_tasks[tid].target_file);  
            }

            active_tasks.Remove(tid);
            schedule();
        }

        public void onTaskProgress(uint tid, double prog)
        {
            Task t = active_tasks[tid];
            if (prog >= 100)
            {
                prog = 100;
                
            }
            t.Progress = prog;
            
        }

        public void onTaskPaused(uint tid)
        {
        }

        public void onEdlSkip(uint tid,double st)
        {
            /*if (st >= active_tasks[tid].video.trim_end)
            {
                active_tasks[tid].State = TaskState.TaskState_Done;
                exe.stop(tid);// 可能造成输出文件无法播放
            }*/
        }
    }


    public class EncoderCallback : I_console_callback_t
    {
        Encoder encoder;


        public EncoderCallback(Encoder e)
            : base()
        {
            encoder = e;
        }

        protected override void on_exit(uint id, IntPtr code)
        {
            try
            {
                encoder.dispatcher.BeginInvoke((Action)(() =>
                {
                    encoder.onTaskExit(id, code.ToInt32());
                }));
            }
            catch (System.Reflection.TargetInvocationException e)
            {
                Debug.Write(e.InnerException);

            }
            catch (Exception e)
            {
                Debug.Write(e);
            }
        }

        protected override void on_stderr(uint id, IntPtr param, string str)
        {
            try
            {
                Debug.Write("[err=>]" + str);
                encoder.dispatcher.BeginInvoke((Action)(() =>
                {
                    encoder.addErrorString(id, str);
                }));
            }
            catch (Exception e)
            {
                Debug.Write(e);
            }
        }
        protected override void on_stdout(uint id, IntPtr param, string str)
        {
            try
            {
                Debug.Write("[out=>]" + str);
                Match match = Regex.Match(str, @"VIDEO:\s*\[.*?\]\s*([0-9]+)x([0-9]+).*", RegexOptions.IgnoreCase);
                if (match.Success)
                {
                    int w = int.Parse(match.Groups[1].Value);
                    int h = int.Parse(match.Groups[2].Value);

                    encoder.dispatcher.BeginInvoke((Action)(() =>
                    {
                        // outt.set_video_size(w, h);
                    }));
                    return;
                }

                //Pos:   4.6s    233f (37%) 37.99fps Trem:   0min   0mb  A-V:-0.034 [32:127]
                match = Regex.Match(str, @"Pos:\s*[0-9\.]+s\s*[0-9\.]+f\s*\(([0-9\.\s]+)%\)\s*.*");
                //match = Regex.Match(str, @"V:\s*([0-9]{1,}[.][0-9]*)");
                if (match.Success)
                {
                    double prog = double.Parse(match.Groups[1].Value);
                    encoder.dispatcher.BeginInvoke((Action)(() => { encoder.onTaskProgress(id, prog); }));
                    return;
                }

                match = Regex.Match(str, @"FATAL:(.*)");
                if (match.Success)
                {
                    string err = match.Groups[1].Value;
                    encoder.dispatcher.Invoke((Action)(() => { encoder.addErrorString(id, err); }));
                    return;
                }

                //EDL SKIP: Start: 5.20  End: 99999.00   Current: V: 5.24  A: 0.02     
                match = Regex.Match(str, @"EDL SKIP:\s*Start:\s*([0-9\.]+)\s*End:.*");
                if (match.Success)
                {
                    double st = double.Parse(match.Groups[1].Value);
                    encoder.dispatcher.BeginInvoke((Action)(() => { encoder.onEdlSkip(id, st); }));
                }

                if (str.IndexOf("=====  PAUSE  =====") >= 0)
                {
                    encoder.dispatcher.BeginInvoke((Action)(() => { encoder.onTaskPaused(id); }));
                    return;
                }

                //encoder.dispatcher.Invoke((Action)(() => { encoder.parse_stdout(str); }));

                // outt.Dispatcher.Invoke((Action)(() => { outt.set_caption(str); }));
            }
            catch (Exception e)
            {
                Debug.Write(e);
            }
        }
    
        
    };
}
